#include <UDTServer.h>
#include <Poco/Net/SocketAddress.h>

#ifndef WIN32
#include <cstdlib>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include <iostream>
using namespace std;

UDTServer::UDTServer(void)
        :Start(this , &UDTServer::StartImp )
		,QueueClient(this , &UDTServer::QueueClientImp )
		,Replay(this , &UDTServer::ReplayImpl )
		,iServerPort(9000)
{
//	UDT::startup();

}

UDTServer::~UDTServer(void)
{
}



void UDTServer::StartImp()
{
	try
	{
		//Poco::Net::SocketAddress aAddr("127.0.0.1" , iServerPort);

		iServerSocket = UDT::socket(AF_INET, SOCK_STREAM, 0);


		sockaddr_in my_addr;
		my_addr.sin_family = AF_INET;
		my_addr.sin_port = htons(iServerPort);
		my_addr.sin_addr.s_addr = INADDR_ANY;
		memset(&(my_addr.sin_zero), '\0', 8);


		if (UDT::ERROR == UDT::bind(iServerSocket, (sockaddr*)&my_addr, sizeof(my_addr)))
		{
			cout << "bind: " << UDT::getlasterror().getErrorMessage() << endl;
			return ;
		}
		cout << "server is ready at port: " << iServerPort << endl;
		int mss = 9000;
                UDT::setsockopt(iServerSocket, 0, UDT_MSS, &mss, sizeof(int));
		UDT::listen(iServerSocket, 32000);

		sockaddr_storage clientaddr;
		int addrlen = sizeof(clientaddr);

		UDTSOCKET recver;

		while (true)
		{
			if (UDT::INVALID_SOCK == (recver = UDT::accept(iServerSocket, (sockaddr*)&clientaddr, &addrlen)))
			{
				cout << "accept: " << UDT::getlasterror().getErrorMessage() << endl;
				return ;
			}
			Poco::Net::SocketAddress aAddr((sockaddr*)&clientaddr , addrlen );
			cout<<"incoming connection: "<<aAddr.toString()<<endl;
			QueueClient(recver);
		}

		UDT::close(iServerSocket);


	}
	catch (...)
	{
		
	}
}

void UDTServer::Stop()
{

}

void UDTServer::Setup(Poco::AutoPtr<Poco::Util::AbstractConfiguration> aConfiguration)
{
	iServerPort = aConfiguration->getInt("UDTServerPort" , iServerPort);
}

//#include <Poco/NumberFormatter.h>
void UDTServer::QueueClientImp(const UDTSOCKET& aClientSocket)
{
	// get session ID
	Poco::UInt64 aSessionID = 0;
	UDT::recv(aClientSocket , (char*)(&aSessionID) , sizeof(aSessionID), 0 ) ;
	// can get more information for security (validate session, authenticate...)
	int mss = 9000;
        UDT::setsockopt(aClientSocket, 0, UDT_MSS, &mss, sizeof(int));
	// Create named mutex
	//std::string aMutexName = "";
	Poco::SharedPtr <UDTSOCKET> aFirstSocket ;

	{
		Poco::Mutex::ScopedLock aLock(iCacheMutex);
		if (aFirstSocket = iSession2FirstSocketCache.get(aSessionID))
		{
			iSession2FirstSocketCache.remove( aSessionID );
		}
		else
			iSession2FirstSocketCache.add(aSessionID , aClientSocket);
	}

	if (aFirstSocket) // this socket is the second socket of this session
	{
		MakeFriends(*aFirstSocket , aClientSocket);
	}
	
}

#include <Poco/NumberFormatter.h>
void UDTServer::MakeFriends(UDTSOCKET aFirstSocket , UDTSOCKET aSecondSocket)
{
	sockaddr_storage clientaddr;
	int addrlen = sizeof(clientaddr);

	UDT::getpeername(aFirstSocket , (sockaddr*)&clientaddr, &addrlen );
	Poco::Net::SocketAddress aAddr( (sockaddr*)&clientaddr, addrlen) ;
	std::string aFirstAddr = aAddr.toString();
	// get local addr of the first peer
	std::string aLocalInfo1;
	if (!ReceiveData(aFirstSocket , aLocalInfo1  ) )
		return;
	///

	UDT::getpeername(aSecondSocket , (sockaddr*)&clientaddr, &addrlen );
	Poco::Net::SocketAddress aAddr2( (sockaddr*)&clientaddr, addrlen) ;
	std::string aSecondAddr = aAddr2.toString();
	// get Local addr of the secon peer
	std::string aLocalInfo2;
	if (!ReceiveData(aSecondSocket , aLocalInfo2 ) )
		return;


	
	std::string aPeerData = aFirstAddr + " " + aSecondAddr + " " + aLocalInfo2;  // to first peer


	//UDT::send(aFirstSocket , aPeerData.data() , aPeerData.length() , 0); // can be optimized later
	SendData(aFirstSocket , aPeerData);
	std::cout<< aPeerData<<std::endl;

	aPeerData = aSecondAddr + " " + aFirstAddr+ " " + aLocalInfo1;


	//UDT::send(aSecondSocket , aPeerData.data() , aPeerData.length() , 0); // can be optimized later
	SendData( aSecondSocket , aPeerData );
	std::cout<< aPeerData<<std::endl;

	Replay(TReplayParam( aFirstSocket ,aSecondSocket ) );
	Replay(TReplayParam( aSecondSocket , aFirstSocket) );


}
void UDTServer::ReplayImpl(const TReplayParam& aSocketPair)
{
	UDTSOCKET aSenderSock = aSocketPair.get<0>();
	UDTSOCKET aReceiverSock = aSocketPair.get<1>();
	char aDataBlock[64000];
	while (true)
	{
		int aSb = 0;
		if (UDT::ERROR == (aSb = UDT::recv(aSenderSock , aDataBlock , sizeof(aDataBlock) , 0) ) )
		{
		cout<<"Ngat ket noi"<<endl;
			return;
		}
		//if (UDT::ERROR == UDT::send(aSenderSock , aDataBlock , sizeof(aDataBlock) , 0) )
		aDataBlock[aSb] = 0;
		cout.write(aDataBlock , aSb);
		if (!UDTServer::SendData(aReceiverSock , aDataBlock , aSb) )
			return;

	}
}

bool UDTServer::SendData(UDTSOCKET aSocket,const char* aData, int size)
{
	int ssize = 0;
	int ss;
	while (ssize < size)
	{
		if (UDT::ERROR == (ss = UDT::send(aSocket, aData + ssize, size - ssize, 0)))
		{
			cout << "send:" << UDT::getlasterror().getErrorMessage() << endl;
			return false;
		}

		ssize += ss;
	} 

	return true;
}

bool UDTServer::SendData(UDTSOCKET aSocket , const std::string& aData)
{
	Poco::UInt16 aLength = aData.length();
	if (!SendData(aSocket , (char*)&aLength , sizeof(aLength) ) )
		return false;

	return SendData(aSocket , aData.data() , aData.length() );
}

bool UDTServer::ReceiveData(UDTSOCKET aSocket , char* aData, int size)
{
	int rsize = 0;
	int rs;
	while (rsize < size)
	{
		if (UDT::ERROR == (rs = UDT::recv(aSocket, aData + rsize, size - rsize, 0)))
		{
			cout << "recv:" << UDT::getlasterror().getErrorMessage() << endl;
			return false;

		}

		rsize += rs;
	}
}

bool UDTServer::ReceiveData(UDTSOCKET aSocket , std::string& aData)
{
	Poco::UInt16 aLength;
	if (!ReceiveData(aSocket , (char*)&aLength , sizeof (aLength) ) )
		return false;
	aData.resize(aLength );
	return ReceiveData(aSocket , const_cast<char*>(aData.data() ) , aLength );

}

